;UZDUOTIS
;Dalybos is nulio pertraukimo (int 0) apdorojimo procedura. 
;Si procedura turi i ekrana isvesti informacija apie pertraukima sukelusia operacija: 
;adresa, koda, mnemonika, operandu reiksmes.
;       Pvz.: i ekrana isvedama informacija galetu atrodyti taip: 
;             Dalyba is nulio! 0000:0128  F7F3   div bx ; dx= 0001, ax= 2532, bx = 0000

;DIREKTYVA KOMPILIATORIUI
;nurodomas programos modelis (mazas) 64KB duomenu segmentui ir 64KB kodo segmentui
.model small

;DIREKTYVA KOMPILIATORIUI
;isskirti steko segmentui 256 baitus (100h 16-tainej sistemoj)
.stack 100h         

;DUOMENU SEGMENTAS----------------------------------------------------------------
;eiluciu pranesimai uzbaigiami simboliu $, kad DOS funkcijos eidamos per eilutes zinotu
;kur baigiasi eilute, t.y. kad nespauzdintu visos atminties be galo. Sutikus $ spauzdinimas
;nutraukiamas.
;db reiskia kad atmintis isskiriama baitais (kiti variantai pvz dw - zodziui (2 baitai))
;dup reiskia kad po jo einantis simbolis atkartojamas kazkiek kartu
;(?) reiskia kad atmintis nera inicializuojama, t.y. jai nesuteikiama pradine verte
.data
    Zero db 0
    Zero1 db 0
    Msg db 'Dalyba is nulio! $'
    MsgAX db ' AX=$'
    MsgBX db ' BX=$'
    MsgCX db ' CX=$'
    MsgDX db ' DX=$'
    MsgSI db ' SI=$'
    MsgDI db ' DI=$'
    MsgBP db ' BP=$'
    MsgDiv db ' DIV $'
    MsgIDiv db ' IDIV $'
    Comma db ',$'
    Space db ' $'
    Colon db ':$'
    Plus  db '+$'
    Left  db '[$'
    Right db ']$'
    
    CmdLength dw 0
    Cmd db 0, 0, 0, 0, 0
    
	;Spauzdinant mnemonika, kad nereiketu nagrineti visu atskiru atveju,
	;Paimami bitai is mod/rm baito padauginami is atitankamo ilgio
	;ir gaunamas poslinkis atminties vietai, kuri nurodo spauzdinamos mnemonikos simbolius

    Mems db '[BX+SI]$'	
         db '[BX+DI]$'
         db '[BP+SI]$'
         db '[BP+DI]$'
         db '[SI]   $'
         db '[DI]   $'
         db '[BP]   $'
         db '[BX]   $'
         
    R16  db 'AX$'
         db 'CX$'
         db 'DX$'
         db 'BX$'
         db 'SP$'
         db 'BP$'
         db 'SI$'
         db 'DI$'
         
    R8   db 'AL$'
         db 'BL$'
         db 'CL$'
         db 'DL$'
         db 'AH$'
         db 'BH$'
         db 'CH$'
         db 'DH$'
         
    Segs db ' ES:$'
         db ' CS:$'
         db ' SS:$'
         db ' DS:$'   
     

;KODO SEGMENTAS-------------------------------------------------------------------
.code                                                      

;----------------------------------------------------------
;Isimena komanda i Cmd komandu kodu eile
;AL - komandos baitas
;----------------------------------------------------------
proc RemindCommand
    push bx             
    mov bx, CmdLength
    mov [Cmd+bx], al
    inc CmdLength
    pop bx
    ret
endp 

;---------------------------------------------------------- 
;Atspauzdina Cmd komandu eile baitais
;----------------------------------------------------------
proc PrintCommand
    push ax
    push bx
    push cx
    
    mov cx, CmdLength
    mov bx, 0
PrintCmdByte:
    mov al, Cmd[bx]
    call PrintByteInHex
    inc bx
    loop PrintCmdByte 
       
    pop cx
    pop bx
    pop ax
    ret
endp

;----------------------------------------------------------
;Atspauzdina komandos DIV arba IDIV mnemonika
;ES:BX - adresas i komandos pradzia 
;----------------------------------------------------------
proc PrintMnemonic
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push bp
    
    ;Tikrinam ar yra priekyje prefiksas
PrefixCheck:
    mov al, es:bx
    and al, 11100111b
    cmp al, 00100110b
    je PrefixRemind
    mov dh, 0           ;Jei ne isimenam ji kaip DH=0
    jmp CmdCheck
PrefixRemind:
    mov al, es:bx
    call RemindCommand  ;Isimenam prefikso komandos koda
    and al, 00011000b   ;Paimam prefiksa
    shr al, 1           ;Nustumiam bitus i desine
    shr al, 1
    shr al, 1
    mov cl, 5           ;Padauginam is 5, nes toks yra prefikso eilutes ilgis (formuojamas adresas)
    mul cl
    mov si, ax          ;Isimenamas prefikso adresas
    mov dh, 1           ;Isimenam kad rastas prefiksas kaip DH=1
    inc bx              ;Pereinam prie sekancio komandos baito
CmdCheck:
    mov al, es:bx       ;Nuskaitom komanda
    call RemindCommand  ;Isimenam komandos koda
    and al, 11111110b
    cmp al, 11110110b
    jne EndMnemonicQuick     ;Jei komandos kodas netinka, ja atmetam net nespauzdine
    mov dl, es:bx
    and dl, 00000001b   ;Nuskaitom w bita
    mov bp, dx          ;Ji isimenam BP registre
    inc bx              ;Pereinam prie sekancio komandos baito
    mov al, es:bx
    and al, 00111000b   
    cmp al, 00110000b   ;Tikrinam ar tai DIV komanda
    je PrintDiv
    cmp al, 00111000b   ;Tikrinam ar tai IDIV komanda
    je PrintIdiv
    jmp EndMnemonic     ;Jei tai nera nei viena is siu komandu, baigiam spauzdinima

EndMnemonicQuick:
    jmp EndMnemonic
    
PrintDiv:
    mov ax, 0900h
    cmp dh, 0
    je SkipSegmentDiv
    lea dx, [si]+Segs   ;Jei DH=1, t.y. buvo segmentas atspauzdinam ji pagal pries tai issaugota jo adresa SI registre, kartu su Segs poslinkiu
    int 21h
SkipSegmentDiv:
    lea dx, MsgDiv
    int 21h             ;Spauzdinam DIV
    jmp EndPrint    

PrintIdiv:
    mov ax, 0900h       
    cmp dh, 0
    je SkipSegmentIDiv  ;Jei DH=1, t.y. buvo segmentas atspauzdinam ji pagal pries tai issaugota jo adresa SI registre, kartu su Segs poslinkiu
    lea dx, [si]+Segs
    int 21h
SkipSegmentIDiv:
    lea dx, MsgIDiv
    int 21h             ;Spauzdinam IDIV
    jmp EndPrint
    
EndPrint:
    mov al, es:bx         
    call RemindCommand   ;Isimendam Mod/RM baita
    and al, 11000000b    ;Nuskaitom Mod bitus ir sokame i atitinkama ju apdoruojima
    cmp al, 00000000b
    je Mod00
    cmp al, 01000000b
    je Mod01
    cmp al, 10000000b
    je Mod10
    jmp Mod11
    
Mod00:                   ;Mod = 00 bitu apdorojimas
    mov al, es:bx
    and al, 00000111b    ;Nuskaitom R/M reiksme
    cmp al, 00000110b    ;Kai Mod = 00 ir R/M = 110 tai reikia kad po komandos is karto eina 16bit poslinkis
    jne SkipDisplacement
    inc bx
    mov al, es:bx        ;Nuskaitom pirma poslinkio baita
    call RemindCommand   ;Isimenam pirma poslinkio baita 
    inc bx
    mov ah, es:bx        ;Nuskaitom antra poslinkio baita
    call RemindCommand   ;Isimenam antra poslinkio baita
    call PrintWordDisp   ;Spauzdinam poslinki
    jmp EndMnemonic      ;Baigiam nagrinejima
SkipDisplacement:
    mov al, es:bx
    and al, 00000111b    ;Nuskaitom R/M reiksme
    mov cl, 8
    mul cl               ;Dauginam ja is 8, nes toks eilutes ilgis spauzdinant atmintires registrus
    mov di, ax
    mov ax, 0900h
    lea dx, [di]+Mems    ;Spauzdinam atminties registrus pagal paskaiciuota DI reiksme (R/M padauginus is 8) ir Mems poslinki
    int 21h
    jmp EndMnemonic      ;Baigiam nagrinejima

Mod01:                  ;Mod = 01 bitu apdorojimas
    mov al, es:bx
    and al, 00000111b   ;Nuskaitom R/M reiksme
    mov cl, 8
    mul cl              ;Dauginam ja is 8, nes toks eilutes ilgis spauzdinant atminties registrus
    mov di, ax
    mov ax, 0900h
    lea dx, [di]+Mems   ;Spauzdinam atminties registrus pagal paskaiciuota DI reiksme (R/M padauginus is 8) ir Mems poslinki
    int 21h
    lea dx, Plus        ;Spauzdinam pliusa
    int 21h
    inc bx              ;Einam prie poslinkio baito
    mov al, es:bx       ;Nuskaitom poslinki
    call RemindCommand  ;Isimendam poslinki
    call PrintByteDisp  ;Spauzdinam poslinki
    jmp EndMnemonic     ;Baigiam nagrinejima

Mod10:                  ;Mod = 10 bitu apdorojimas
    mov al, es:bx
    and al, 00000111b   ;Nuskaitom R/M reiksme
    mov cl, 8
    mul cl              ;Dauginam ja is 8, nes toks eilutes ilgis spauzdinant atminties registrus
    mov di, ax
    mov ax, 0900h
    lea dx, [di]+Mems   ;Spauzdinam atminties registrus pagal paskaiciuota DI reiksme (R/M padauginus is 8) ir Mems poslinki
    int 21h
    lea dx, Plus
    int 21h
    inc bx
    mov al, es:bx       ;Nuskaitom pirma poslinkio baita
    call RemindCommand  ;Isimenam pirma poslinkio baita
    inc bx
    mov ah, es:bx       ;Nuskaitom antra poslinkio baita
    call RemindCommand  ;Isimenam antra poslinkio baita
    call PrintWordDisp  ;Spauzdinam poslinki
    jmp EndMnemonic     ;Baigiam nagrinejima

Mod11:                  ;Mod = 11 bitu apdorojimas
    mov al, es:bx
    and al, 00000111b   ;Nuskaitom R/M reiksme
    mov cl, 3
    mul cl              ;Dauginam ja is 3, nes toks eilutes ilgis spauzdinant registrus
    mov di, ax
    mov dx, bp
    cmp dl, 1           ;Patikrinam ar w=1
    je Mod11Reg16
    mov ax, 0900h       ;Jei w=0, vadinasi spauzdinam apskaiciuota 8bit registra
    lea dx, [di]+R8
    int 21h
    jmp EndMnemonic
Mod11Reg16:             ;Jei w=1, vadinasi spauzdinam apskaiciuota 16bit registra
    mov ax, 0900h
    lea dx, [di]+R16
    int 21h    
    
EndMnemonic:    
    
    pop bp
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    
    ret
endp

;----------------------------------------------------------
;Dalybos is nulio pertraukimo apdorocijimo procedura (INT0)
;----------------------------------------------------------
proc InterruptHandler
    push ax
    push bx
    push cx
    push dx  
    
    ;Info pranesimo spauzdinimas
    mov ax, 0900h       ;DOS funkcija spauzdinanti ($-terminated) eilute i ekrana
    lea dx, Msg         ;Nurodome adresa eilutes, kuria spauzdinsim i ekrana
    int 21h             ;Spauzdinam eilute
    
    ;Adreso gavimas ir spauzdinimas
    mov bx, sp          ;Persikeliam steko rodykle i BX, pop'u nenaudosim, nes steko reiksmiu dar reikes gryztant is sios proceduros
    add bx, 10          ;Padidinam BX dviem, kad galetumem prieiti prie issaugoto CS registro reiksmes
    mov ax, ss:bx       ;I AX ikeliam issaugota CS reiksme
    mov es, ax          ;Issaugom CS reiksme i ES registra, veliau ji panaudosime dalybos komandai isgauti
    call PrintWordInHex ;Atspauzdinam issaugota CS reiksme
    mov ax, 0900h       ;DOS funkcija spauzdinanti ($-terminated) eilute i ekrana
    lea dx, Colon       ;Nurodome adresa eilutes, kuria spauzdinsim i ekrana (dvitaskis tarp segmento ir poslinkio adreso)
    int 21h             ;Spauzdinam dvitaski
    sub bx, 2           ;Sumazinam BX atgal dviem, kad paimtumem  issaugota IP reiksme
    mov ax, ss:bx       ;I AX ikeliam issaugota IP reiksme
    mov bx, ax          ;Isikeliam IP reiksme i BX
    sub bx, 5           ;Sumazinam BX keturiais, nes IP reiksme yra padidinama per komandos ilgi kuri gali buti nuo 2 iki 4, taigi dabar reikes issiaiskinti koks yra komandos ilgis
    
    ;Komandos suradimas
CommandByte:            
    mov si, 0    
    mov dl, es:[bx]       
    and dl, 11100111b   
    cmp dl, 00100110b 
    jne Cmd1Check
    mov si, 1 
Cmd1Check:
    mov dl, es:[bx+si]
    and dl, 11111110b
    cmp dl, 11110110b
    je Cmd2Check
    inc bx
    jmp CommandByte
Cmd2Check:
    mov dl, es:[bx+si]+1
    and dl, 00111000b
    cmp dl, 00110000b
    je Continue
    cmp dl, 00111000b
    je Continue
    inc bx
    jmp CommandByte    

Continue:    
    mov ax, bx
    call PrintWordInHex ;Atspauzdinam issaugota IP reiksme
    
    mov ax, 0900h       ;DOS funkcija spauzdinanti ($-terminated) eilute i ekrana
    lea dx, Space       ;Nurodome adresa eilutes, kuria spauzdinsim i ekrana
    int 21h             ;Atspauzdinam tarpa
    
    ;mov ax, es:bx       ;Siuo momentu ES = buvusiam CS, o BX = buvusiam IP, todel taip galime paimti DIV komandos koda
    ;mov ch, al          ;Apkeiciam jaunesniji ir vyresniji baitus
    ;mov cl, ah          ;Apkeiciam jaunesniji ir vyresniji baitus
    ;mov ax, cx          ;Issaugom apkeistus baitus
    ;call PrintWordInHex ;Atspauzdinam komandos koda
    
    ;Mnemonikos spauzdinimas
    call PrintMnemonic       
    
    mov ax, 0900h       ;DOS funkcija spauzdinanti ($-terminated) eilute i ekrana
    lea dx, Space       ;Nurodome adresa eilutes, kuria spauzdinsim i ekrana
    int 21h             ;Atspauzdinam tarpa
    
    ;Kodo spauzdinimas
    call PrintCommand
    
    ;Atspauzdinam AX=
    mov ax, 0900h
    lea dx, MsgAX
    int 21h
    ;Atspauzdinam AX reiksme
    mov bx, sp
    add bx, 6
    mov ax, ss:bx
    call PrintWordInHex
    
    ;Atspauzdinam BX=
    mov ax, 0900h
    lea dx, MsgBX
    int 21h
    ;Atspauzdinam BX reiksme
    mov bx, sp
    add bx, 4
    mov ax, ss:bx
    call PrintWordInHex
    
    ;Atspauzdinam CX=
    mov ax, 0900h
    lea dx, MsgCX
    int 21h
    ;Atspauzdinam CX reiksme
    mov bx, sp
    add bx, 2
    mov ax, ss:bx
    call PrintWordInHex
    
    ;Atspauzdinam DX=
    mov ax, 0900h
    lea dx, MsgDX
    int 21h
    ;Atspauzdinam DX reiksme
    mov bx, sp
    mov ax, ss:bx
    call PrintWordInHex
    
    ;Atspauzdinam SI=
    mov ax, 0900h
    lea dx, MsgSI
    int 21h
    ;Atspauzdinam SI reiksme
    mov ax, si
    call PrintWordInHex
    
    ;Atspauzdinam DI=
    mov ax, 0900h
    lea dx, MsgDI
    int 21h 
    ;Atspauzdinam DI reiksme
    mov ax, di
    call PrintWordInHex  
    
    ;Atspauzdinam BP=
    mov ax, 0900h
    lea dx, MsgBP
    int 21h 
    ;Atspauzdinam BP reiksme
    mov ax, bp
    call PrintWordInHex
    
    pop dx
    pop cx
    pop bx
    pop ax
    
    jmp Exit    
    iret          ;Gryzimas is pertraukimo proceduros
endp

;----------------------------------------------------------
;Pagalbine procedura 8bit poslinkius spauzdint
;AL - poslinkis
;----------------------------------------------------------
proc PrintByteDisp
    push ax
    
    mov ax, 0900h
    lea dx, Left
    int 21h
    
    pop ax
    push ax
    call PrintByteInHex
    
    mov ax, 0900h
    lea dx, Right
    int 21h
    
    pop ax
    ret
endp

;---------------------------------------------------------- 
;Pagalbine procedura 16bit poslinkius spauzdint
;AX - poslinkis
;----------------------------------------------------------
proc PrintWordDisp
    push ax
    
    mov ax, 0900h
    lea dx, Left
    int 21h
    
    pop ax
    push ax
    call PrintWordInHex
    
    mov ax, 0900h
    lea dx, Right
    int 21h
    
    pop ax
    ret
endp

;----------------------------------------------------------
;Atspauzdina zodi(16-bit) 16-taineje sistemoje formatu xxxx
;jei reikia priekyje darasant nulius.
;AX - zodis kuri reikia spauzdint
;----------------------------------------------------------
proc PrintWordInHex
    push ax
    push bx
    push cx
    push dx
    
    mov bx, 16          ;Is kiek dalinsim
    mov cx, 4           ;Kiek kartu kartosim dalyba
    mov dx, 0           ;DX = 0, nes DX:AX nurodo tai kas bus dalinama, reikalinga tik AX dalis
NextHex:
    div bx              ;AX = DX:AX / BX ir DX - liekana
    push dx             ;Issaugome i steka liekana (t.y. paskutini skaitmeni)
    loop NextHex        ;Kartojam dalinimo cikla
     
    mov ax, 0200h       ;DOS funkcija simbolio spauzdinimui i ekrana    
    mov cx, 4           ;Kiek kartu kartosim cikla
NextChar:    
    pop dx              ;Paimam is steko viena hex'a
    cmp dx, 10          ;Tikrinam ar tai bus skaicius
    jb Digit            ;Jei maziau uz 10, vadinasi reis spauzdint skaiciu, kitu atveju raide
    add dx, 37h         ;Prie DX pridedam 37h, kad gautumem ASCII raides reiksme
    jmp Print
Digit:
    add dx, 30h         ;Prie DX pridedam 30h, kad gautumem ASCII skaitmens reiksme
Print:
    int 21h             ;Spauzdinam simboli
    loop NextChar       ;Einam prie sekancio simbolio
    
    pop dx
    pop cx
    pop bx
    pop ax
         
    ret
endp   

;----------------------------------------------------------
;Atspauzdina baita(8-bit) 16-taineje sistemoje formatu xx
;jei reikia priekyje darasant nulius.
;AL - zodis kuri reikia spauzdint
;----------------------------------------------------------
proc PrintByteInHex
    push ax
    push bx
    push cx
    push dx
    
    mov bl, 16          ;Is kiek dalinsim
    mov cx, 2           ;Kiek kartu kartosim dalyba
    mov ah, 0           ;AH = 0, AX nurodo tai kas bus dalinama, reikalinga tik AL dalis
    mov dx, 0           ;DX = 0
ByteNextHex:
    div bl              ;AL = AX / BL ir AH - liekana
    mov dl, ah          ;Perkeliam liekana i DL, DH = 0
    push dx             ;Issaugome i steka liekana (t.y. paskutini skaitmeni)
    loop ByteNextHex    ;Kartojam dalinimo cikla
     
    mov ax, 0200h       ;DOS funkcija simbolio spauzdinimui i ekrana    
    mov cx, 2           ;Kiek kartu kartosim cikla
ByteNextChar:    
    pop dx              ;Paimam is steko viena hex'a
    cmp dx, 10          ;Tikrinam ar tai bus skaicius
    jb ByteDigit        ;Jei maziau uz 10, vadinasi reis spauzdint skaiciu, kitu atveju raide
    add dx, 37h         ;Prie DX pridedam 37h, kad gautumem ASCII raides reiksme
    jmp BytePrint
ByteDigit:
    add dx, 30h         ;Prie DX pridedam 30h, kad gautumem ASCII skaitmens reiksme
BytePrint:
    int 21h             ;Spauzdinam simboli
    loop ByteNextChar   ;Einam prie sekancio simbolio
    
    pop dx
    pop cx
    pop bx
    pop ax
         
    ret
endp   

;Pagrindine programa
start:
    mov ax, @data              ;I AX registra irasome duomenu segmento paragrafa
    mov ds, ax                 ;Nustatome einamaji duomenu segmenta 
   
    ;Uzregistruojame pertraukimo procedura
    mov bx, 0                  ;BX = 0
    mov es, bx                 ;ES = BX = 0
    
    lea ax, InterruptHandler
    mov es:bx, ax  ;I ES:BX (arba 0000:0000 t.y. dalybos is nulio pertraukimo apdorojimo adresu vieta) irasome proceduros poslinki kodo segmente
    mov bx, 2                             ;BX = 2
    mov es:bx, cs                         ;I ES:BX (arba 0000:0002 t.y. dalybos is nulio pertraukimo apdorojimo adresu vieta) irasome kodo segmenta, kuriame ta procedura yra
    
    ;Testiniai registrai
    mov ax, 1234h
    mov cx, 0000h
    mov bx, 0000h
    mov dx, 0ABCDh
    
    ;Operacija issaukianti pertraukima
    ;Ivairus testai
    ;idiv cx
    ;div bx
    ;div [bx]
    ;div [bx+di] 
    div [si+0001h]
    
Exit:    
    mov ax, 4C00h               ;I AX dedame 4Ch DOS 21h funkcija, kuri iseina is programos
    int 21h                     ;Iseiname is programos 
end start    